import math
import utime
import gc
import ure


class NumberedMusicalNotation:
    def __init__(self, pwm):
        self.pwm = pwm

        self.setMajor('C')

        self.beatSpeed = 120
        self.beatNumber = 4
        self.preAdd = 0

        self.lastFundamentalToneRate = 0

    def tone(self, freq):
        self.pwm.duty(50)
        self.pwm.freq(freq)

    def noTone(self):
        self.pwm.duty(0)

    def play(self, rate, timeValue):
        hitTime = 0.8 * timeValue
        voiceTime = timeValue - hitTime

        if(rate != 0):
            self.tone(int(rate * math.pow(2, self.preAdd)))
            utime.sleep_ms(int(hitTime * 1000))
            self.noTone()
            utime.sleep_ms(int(voiceTime * 1000))
        else:
            self.noTone()
            utime.sleep_ms(int(timeValue * 1000))

        self.noTone()

    def setSpeed(self, beatSpeed):
        self.beatSpeed = beatSpeed

    def setTimeSignature(self, beatNumber):
        self.beatNumber = beatNumber

    def setPreAdd(self, p):
        self.preAdd = p

    def setMajor(self, major):
        majorDict = {
            'C': 262,
            '#C': 277,
            'bD': 277,
            'D': 294,
            '#D': 311,
            'bE': 311,
            'E': 330,
            'F': 349,
            '#F': 370,
            'bG': 370,
            'G': 392,
            '#G': 415,
            'bA': 415,
            'A': 440,
            '#A': 466,
            'bB': 466,
            'B': 494
        }

        frequency = []

        for i in range(12):
            f = majorDict[major] * math.pow(2, i/12)
            frequency.append(f)

        self.scaleDict = {
            '0': 0,
            '1': int(frequency[0]),
            'b2': int(frequency[1]),
            '2': int(frequency[2]),
            'b3': int(frequency[3]),
            '3': int(frequency[4]),
            '4': int(frequency[5]),
            'b5': int(frequency[6]),
            '5': int(frequency[7]),
            'b6': int(frequency[8]),
            '6': int(frequency[9]),
            'b7': int(frequency[10]),
            '7': int(frequency[11])
        }

    def playNotation(self, monophonic):
        scaleKey = ''

        if(monophonic.find('b') != -1):
            scaleKey += 'b'

        digitals = ure.search(r'\d+', monophonic)
        scaleKey += digitals.group(0)
        riseNumber = monophonic.count('+')
        dropNumber = monophonic.count('-')
        extendNumber = monophonic.count('.')
        reduceNumber = monophonic.count('_')
        dottedNumber = monophonic.count('*')
        upWaveNumber = monophonic.count('~')

        # 计算基本音频率
        realRate = self.scaleDict[scaleKey] * \
            math.pow(2, riseNumber) * (1 / math.pow(2, dropNumber))

        # 计算四分音符时值
        quarterNote = (60 / self.beatSpeed) * self.beatNumber * (1.0 / 4)

        # 计算基本音时值
        timeValue = quarterNote * (1 / math.pow(2, reduceNumber))

        # 计算附点时值
        if(dottedNumber > 0):
            timeValue = timeValue * 1.5

        # 计算延长音时值
        timeValue += quarterNote * extendNumber

        if(upWaveNumber == 0):
            self.play(realRate, timeValue)
        else:
            self.play(realRate, timeValue * 3.0 / 8)
            self.play(self.lastFundamentalToneRate, timeValue * 1.0 / 4)
            self.play(realRate, timeValue * 3.0 / 8)

        self.lastFundamentalToneRate = realRate

        gc.collect()
